package edu.cmsc611.textsimulator;
import java.util.*;
import java.lang.*;
import java.awt.*;

import edu.cmsc611.textsimulator.Instruction.regType;
import edu.cmsc611.textsimulator.Registers.registerMode;

/**
 * WriteBack handles the execution of the writeback stage of pipelined execution.
 */
class WriteBack extends Stage {
	/**
	 * indicates whether the program has completed execution.
	 */
	private boolean done;
	/**
	 * the number of instruction in the program.
	 */
	public int numInstructions;

	public Stages stg;
	OutputChart tablechart=null;
	/**
	 * creates a WriteBack object and initialzes its fields.
	 */
	public WriteBack() {
		super();
		done = false;
		numInstructions = 0;
	}

	/**
	 * process the current instruction.  Implements the functionality of the writeback stage of execution.
	 * Uses a MemoryType, the vector of instructions, the GUI's ListBoxes, and a boolean indicating
	 * whether all of the previous stages contain NOPs.
	 * @see MemoryType
	 * @see Instruction
	 * @see ListBoxes
	 */
	public void step(MemoryType regFile, Vector Instructions, DispInfo lb, boolean allNOPs,Stages _stg) {

		String str;
		stg=_stg;
		if (myInstruction.wbFlush == true)
			return;

		if (myInstruction.opcode != 0)
			numInstructions++;

		switch( myInstruction.opcode) {
		case 0:   // NOP
		case 43:  // SW
		case 82: //HLT
		case 81: //J
		case 70: //BEQ
		case 71: //BNE
		//case 70:  // BEQ
		//case 71:  // BNE
			break;
		default:
			// write to dest register (if not $0)
			if ((myInstruction.rd != 0) && (myInstruction.opcode != 0)&& myInstruction.registerType.equals(Instruction.regType.R))
				if (myInstruction.flush == false) {
					regFile.putValue( myInstruction.rdValue,myInstruction.rd );
					if (myInstruction.rd < 10)
						str = "R0"+myInstruction.rd+": "+myInstruction.rdValue;
					else
						str = "R"+myInstruction.rd+": "+myInstruction.rdValue;
					lb.RF.replaceItem(str, myInstruction.rd);

				}
		}
		if(!myInstruction.instructionString.equals("NOP"))
		{
			if(!(myInstruction.opcode==82||myInstruction.opcode==80||myInstruction.opcode==81||myInstruction.opcode==70||myInstruction.opcode==71))
			{
			tablechart=OutputChart.getInstance();
			Utilities utils=Utilities.getInstance();
			int iteration=myInstruction.getIteration_index()*Instructions.size();
			//int iteration=utils.getLoopiteration()*Instructions.size();
			tablechart.setvalue(myInstruction.getInstruction_index()+iteration+1, 4, Integer.toString(stg.nCycles));
			//System.out.println(myInstruction.getInstruction_index()+iteration+"instruciton to wirteback " +Integer.toString(stg.nCycles));
			}
		}
		// check to see if this is last instruction
		if ((PC == (Instructions.size()-1)) && allNOPs) {
			done = true;
			lb.Messages.addItem("Program done.", 0);
		}
	}

	/**
	 * returns a boolean indicating whether the program has completed execution.
	 */
	public boolean programDone()
	{
		return done;
	}

	/**
	 * returns a string representation of the current instruction and the results of its processing.
	 */
	public String toString() {
		String temp;

		if (myInstruction.wbFlush == true) {
			temp = "FLUSHED: \n" + myInstruction + "\n";
			return temp;
		}

		switch(myInstruction.opcode) {
		case 0:  // NOP
			temp = myInstruction + "\n";
			break;
		case 43:  // SW
		case 70:  // BEQ
		case 71:  // BNE
			temp = Integer.toString(PC) + ":\n" + myInstruction + "\n";
			break;
		default:
			if ((myInstruction.rd != 0) && (myInstruction.opcode != 0)) {
				temp = Integer.toString(PC) + ":\n" + myInstruction + "\n" +
						"Wrote $" + Integer.toString(myInstruction.rd) +
						"= "+ Integer.toString(myInstruction.rdValue) + "\n";
			} else
				temp = Integer.toString(PC) + ":\n" + myInstruction + "\n";

		}
		return temp;
	}
	public void releaseResources(){
		/**
		 *REGISTER FORMAT
		 * LW OR SW DESTINATION VALUE(SOURCE) AND DEFINED BY rd and rt
		 * DADDI F1,F2,4 rd=F1, rs=F2
		 * ADD.D F1,F2,F3 F1=rd,rs=F2, rt=F3
		 */

		Registers reg=Registers.getInstance();
		if((myInstruction.opcode==24||myInstruction.opcode==26||myInstruction.opcode==32
				||myInstruction.opcode==34||myInstruction.opcode==36||myInstruction.opcode==38
				||myInstruction.opcode==50||myInstruction.opcode==51))
		{
			//DSUB=24,DADD=26,AND=32 OR=34 ADD.D=36 MUL.D=38 DIV.D=50 SUB.D=51
			if(myInstruction.rdtype==regType.R)
			{
				reg.setIsRegisterBusyR(myInstruction.rd, false);
				reg.setRMode(myInstruction.rd, registerMode.Idle);
			}
			else 
				{
				reg.setIsRegisterBusyF(myInstruction.rd, false);
				reg.setFMode(myInstruction.rd, registerMode.Idle);
				}

			if(myInstruction.rstype==regType.R)
			{
				reg.setIsRegisterBusyR(myInstruction.rs, false);
			}
			else 
				{
				reg.setIsRegisterBusyF(myInstruction.rs, false);
				}

			if(myInstruction.rttype==regType.R)
			{
				reg.setIsRegisterBusyR(myInstruction.rt, false);
			}
			else 
				{
				reg.setIsRegisterBusyF(myInstruction.rt, false);
				}
		}
		else if(myInstruction.opcode==40||myInstruction.opcode==42
				||myInstruction.opcode==44||myInstruction.opcode==46)
		{
			//DADDI=40 DSUBI=42 ANDI=44 ORI=46
			if(myInstruction.rdtype==regType.R)
				{
				reg.setIsRegisterBusyR(myInstruction.rd, false);
				reg.setRMode(myInstruction.rd, registerMode.Idle);
				}
			else 
			{
				reg.setIsRegisterBusyF(myInstruction.rd, false);
				reg.setFMode(myInstruction.rd, registerMode.Idle);
			}

			if(myInstruction.rstype==regType.R)
				{
				reg.setIsRegisterBusyR(myInstruction.rs, false);
				}
			else 
				{
				reg.setIsRegisterBusyF(myInstruction.rs, false);
				}


		}
		else if(myInstruction.opcode==48||myInstruction.opcode==54
				||myInstruction.opcode==56||myInstruction.opcode==58)
		{
			//LW=48 SW=54 L.D=56 S.W=58
			if(myInstruction.rdtype==regType.R)
				{
				reg.setRMode(myInstruction.rd, registerMode.Idle);
				reg.setIsRegisterBusyR(myInstruction.rd, false);
				}
			else 
				{
				reg.setFMode(myInstruction.rd, registerMode.Idle);
				reg.setIsRegisterBusyF(myInstruction.rd, false);
				}



			if(myInstruction.rttype==regType.R)
				{
				reg.setIsRegisterBusyR(myInstruction.rt, false);
				}
			else 
				{
				reg.setIsRegisterBusyF(myInstruction.rt, false);
				}
		}
	}
	/**
	 * returns an integer representing the number of instructions executed (not NOPs or instructions
	 * that have been flushed).
	 */
	public int getNumberExecutedInstructions()
	{
		return numInstructions;
	}


}
